package com.franklin.icharm.service;

import java.io.File;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import ti.android.ble.common.BleDeviceInfo;
import ti.android.ble.common.BluetoothLeService;
import ti.android.util.CustomToast;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.provider.CalendarContract.Calendars;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;

import com.android.internal.telephony.ITelephony;
import com.franklin.icharm.R;
import com.franklin.icharm.UserConfig;
import com.franklin.icharm.sClockEmail;
import com.franklin.icharm.scReminder;


import com.google.android.gm.contentprovider.GmailContract;
import com.nichtemna.takeandsavephoto.PhotoActivity;



public class MainService extends Service {
	public class LocalBinder extends Binder {
		public MainService getService() {
			return MainService.this;
		}
	}

	public static  int myProjectCode = 1; // 1 for icharm


	public static final int CALL_RINGING = 0;
	public static final int CALL_OFFHOOK = 1;
	public static final int CALL_ONHOOK = 2;

	public static final String INCOMING_REMINDER_NAME = "INCOMING_REMINDER_NAME";
	public static final String INCOMING_EMAIL_NAME = "INCOMING_EMAIL_NAME";
	public static final String INCOMING_SMS_NAME = "INCOMING_SMS_NAME";
	public static final String INCOMING_CALL_NAME = "INCOMING_CALL_NAME";


	public static final String PACKAGE_NAME = "com.franklin.icharm.service";
	public static final String ACTION_SERVICE_STARTED = PACKAGE_NAME + ".action_service_started";
	public static final String ACTION_APP_UPDATECONFIG = PACKAGE_NAME + ".action_app_updateconfig";
	public static final String ACTION_APP_BLE_SCANNING = PACKAGE_NAME + ".action_app_ble_scanning";
	public static final String ACTION_APP_GO_CONNECTION = PACKAGE_NAME + ".action_app_go_connection";
	public static final String ACTION_UPDATE_LOST_LINK = PACKAGE_NAME + ".action_update_lost_link";
	public static final String ACTION_SCANNED_DEVICE = PACKAGE_NAME + ".action_scanned_device";
	public static final String ACTION_CONNECTED_DEVICE =PACKAGE_NAME + ".action_connected_device";
	public static final String ACTION_DISCONNECTED_DEVICE = PACKAGE_NAME + ".action_disconnected_device";

	public static final String EXTRA_ENABLE = PACKAGE_NAME + ".extra_enable";
	public static final String EXTRA_DEVICE_NAME = PACKAGE_NAME + ".extra_device_name";
	public static final String EXTRA_DEVICE_ADDRESS = PACKAGE_NAME + ".extra_device_address";
	public static final String EXTRA_DEVICE_RSSI = PACKAGE_NAME + ".extra_device_rssi";


	public static final int MEDIACONTROL_KEY_CAMERA = KeyEvent.KEYCODE_CAMERA;
	public static final int MEDIACONTROL_KEY_PLAY = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
	public static final int MEDIACONTROL_KEY_NEXT = KeyEvent.KEYCODE_MEDIA_NEXT;
	public static final int MEDIACONTROL_KEY_PREV = KeyEvent.KEYCODE_MEDIA_PREVIOUS;






	public static String USERCFG_BLE_DEVICE_ADDRESS = "usercfg_ble_device_address";
	public static String USERCFG_ENABLED = "usercfg_enabled";

	private static Context mContext;
	private static final String TAG = "MainService";

	private IntentFilter appFilter;

	// BLE management
	private boolean mBleSupported = true;
	private boolean mScanning = false;
	private int mNumDevs = 0;
	private List<BleDeviceInfo> mDeviceInfoList;
	private static BluetoothManager mBluetoothManager;
	private static BluetoothAdapter mBtAdapter = null;
	private static BluetoothDevice mBluetoothDevice = null;	
	private static BluetoothLeService mBluetoothLeService = null;
	private static BluetoothGatt mBtGatt = null;
	private BluetoothLeService mBtLeService = null;
	private static final int GATT_TIMEOUT = 100; // milliseconds
	public static final int WATCHDOG_TIMEOUT = 10 * 1000; // 1 min
	public String connectedDeviceAddress = null;
	private static boolean mIsGattConnected = false;

	private IntentFilter mFilter;
	private String [] mDeviceFilter = null;
	private List<BluetoothGattService> mServiceList =  new ArrayList<BluetoothGattService>();
	private volatile InternalHandler myHandler ;
	private volatile Looper mServiceLooper;
	private Timer myWatchdogTimer = null;
	private boolean mServicesRdy = false;
	private ArrayList<BLEDataOut>  bleDataout = null;

	private UserConfig mUserConfig ;

	private Watch myWatch = null;
	private boolean mScanOnly = false;

	private Timer inCallTimer = null;

	private final int RESEND_LED_TIME = 10000 ; // resend the group 


	private String emailAcc ;
	private EmailContentObserver emailObserver;
	private ReminderContentObserver reminderObserver;


	public MainService() {
	}

	private final IBinder binder = new LocalBinder();


	@Override
	public IBinder onBind(Intent intent) {
		// TODO: Return the communication channel to the service.
		return binder;
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		// TODO Auto-generated method stub
		super.onConfigurationChanged(newConfig);
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		mContext = this;
		mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

		HandlerThread thread = new HandlerThread("ServiceStartArguments",
				Process.THREAD_PRIORITY_BACKGROUND);
		thread.start();
		mServiceLooper = thread.getLooper();
		myHandler = new InternalHandler(mServiceLooper);

		// Use this check to determine whether BLE is supported on the device. Then
		// you can selectively disable BLE-related features.
		if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
			Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_LONG).show();
			mBleSupported = false;
		}

		// Initializes a Bluetooth adapter. For API level 18 and above, get a
		// reference to BluetoothAdapter through BluetoothManager.
		mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
		mBtAdapter = mBluetoothManager.getAdapter();

		// Checks if Bluetooth is supported on the device.
		if (mBtAdapter == null) {
			Toast.makeText(this, R.string.bt_not_supported, Toast.LENGTH_LONG).show();
			Log.d(TAG, getResources().getString(R.string.bt_not_supported));
			mBleSupported = false;
		}else {
			// Create GATT object
		}
		if (mBtAdapter.isEnabled())
		{
			startBluetoothLeService();
		}
		bleReceiverInit();

		Intent intent = new Intent();
		intent.setAction(MainService.ACTION_SERVICE_STARTED);
		sendBroadcast(intent);		
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		closeReceiver();
		unregisterReceiver(bleReceiver);
		unregisterReceiver(appReceiver);
		hideNotification();
		if (mBtGatt != null)
		{
			mBtGatt.close();
			mBtGatt = null;
		} // if
		mIsGattConnected = false;
		mUserConfig.setConf(UserConfig.KV_DEVICE_CONNECTED, false);
		Log.i(TAG, "MainService destroyed");
	}

	public void onDisconnected() {
		closeReceiver();
		mUserConfig.setConf(UserConfig.KV_DEVICE_CONNECTED, false);
		showNotification("iCharm disconnected");
		if (bleDataout != null)
			bleDataout.clear();
		if (myWatchdogTimer != null){
			myWatchdogTimer.cancel();
		}

				if (mBtGatt != null) {
					Log.i(TAG, "onDisconnected");
//					mBtGatt.disconnect();
					mBtGatt.close();
					mBtGatt = null;
				}
		//		mBluetoothDevice = null;
		//		if (mBluetoothLeService != null)
		//		{
		//			mBluetoothLeService.disconnect(connectedDeviceAddress);
		//		}

	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		Log.i(TAG, "onStartCommand");
		mUserConfig = new UserConfig(this);
		myWatch = new Watch();
		appReceiverInit();
		return START_STICKY;
	}

	private void bleReceiverInit()
	{
		// Register the BroadcastReceiver
		mFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
		mFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
		mFilter.addAction(BluetoothLeService.ACTION_DATA_NOTIFY);
		mFilter.addAction(BluetoothLeService.ACTION_DATA_WRITE);
		mFilter.addAction(BluetoothLeService.ACTION_DATA_READ);
		mFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
		mFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
		registerReceiver(bleReceiver, mFilter);

	}

	private void appReceiverInit()
	{
		// Register the BroadcastReceiver
		appFilter = new IntentFilter(MainService.ACTION_APP_UPDATECONFIG);
		appFilter.addAction(MainService.ACTION_APP_BLE_SCANNING);
		appFilter.addAction(MainService.ACTION_APP_GO_CONNECTION);
		appFilter.addAction(MainService.ACTION_UPDATE_LOST_LINK);
		registerReceiver(appReceiver, appFilter);
	}

	private boolean bRecevierd = false;
	//TODO: callin
	private void initReceiver()
	{
		if (bRecevierd)
			return;
		registerReceiver(mCaller, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED));

		// SMS
		IntentFilter mIntentFilter = new IntentFilter();
		mIntentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
		registerReceiver(mSMS, mIntentFilter);

		// email
		emailAcc = mUserConfig.getConfString(UserConfig.KV_USER_EMAIL, null ); // "fep.hksw@gmail.com"; //PreferenceManager.getDefaultSharedPreferences(this).getString("sc_email", "");
		if (emailAcc.length() > 0){
			final Uri labelsUri = GmailContract.Labels.getLabelsUri(emailAcc);
			emailObserver = new EmailContentObserver(new Handler());
			getContentResolver().registerContentObserver(labelsUri, true, emailObserver );
		}else
		{
			emailObserver = null;
		}


		// reminder
		reminderObserver = new ReminderContentObserver(new Handler());
		getContentResolver().registerContentObserver(Calendars.CONTENT_URI, true, reminderObserver );
		reloadReminders();


		bRecevierd = true;
	}

	private void closeReceiver()
	{
		if (bRecevierd)
		{
			unregisterReceiver(mSMS);
			unregisterReceiver(mCaller);
			if (emailObserver != null)
				getContentResolver().unregisterContentObserver(emailObserver);
			if (reminderObserver != null)
				getContentResolver().unregisterContentObserver(reminderObserver);

			if (reminderTimer != null)
			{
				reminderTimer.cancel();
			}
			bRecevierd = false;
		}
	}

	private void restartBluetoothLeService()
	{
		stopBluetoothLeService();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		startBluetoothLeService();
	}

	private void stopBluetoothLeService(){
		Intent bindIntent = new Intent(this, BluetoothLeService.class);
		stopService(bindIntent);
		unbindService(mServiceConnection);

	}

	private void startBluetoothLeService() {
		boolean f;

		Intent bindIntent = new Intent(this, BluetoothLeService.class);
		startService(bindIntent);
		f = bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
		if (f)
			Log.d(TAG, "BluetoothLeService - success");
		else {
			Log.d(TAG, "BluetoothLeService - failue");

			CustomToast.middleBottom(this, getString(R.string.bt_bind_failed));
			//finish();
		}
	}

	private boolean scanLeDevice(boolean enable) {
		if (enable) {
			if (mScanning)
			{
				mBtAdapter.stopLeScan(mLeScanCallback);
			}
			mScanning = mBtAdapter.startLeScan(mLeScanCallback);
		} else {
			mScanning = false;
			mBtAdapter.stopLeScan(mLeScanCallback);
		}
		return mScanning;
	}
	private void discoverServices() {


		if (mBtGatt == null)
		{
			Log.i(TAG, "mBtGatt error");
			return ;

		}
		if (mBtGatt.discoverServices()) {
			Log.i(TAG, "START SERVICE DISCOVERY");
			mServiceList.clear();
			Log.i(TAG, "Service discovery started");
		} else {
			Log.i(TAG, "Service discovery start failed");
		}
	}

	private void displayServices() {
		mServicesRdy = true;

		try {
			mServiceList = mBtLeService.getSupportedGattServices();

		} catch (Exception e) {
			e.printStackTrace();
			mServicesRdy = false;
		}

		//TODO: connected to send broadcast
		Intent intent = new Intent();
		intent.setAction(MainService.ACTION_CONNECTED_DEVICE);
		//		intent.putExtra(MainService.EXTRA_DEVICE_NAME, device.getName());
		//	intent.putExtra(MainService.EXTRA_DEVICE_ADDRESS, device.getAddress());
		//intent.putExtra(MainService.EXTRA_DEVICE_RSSI, rssi);
		sendBroadcast(intent);		
		mUserConfig.setConf(UserConfig.KV_DEVICE_CONNECTED, true);

		showNotification("iCharm Connected");

		// Characteristics descriptor readout done
		if (mServicesRdy) {
			Log.i(TAG, "Service discovery complete");
			//setStatus(mServiceList.toString());
			//android.os.Debug.waitForDebugger();

			watchInit();
		} else {
			Log.i(TAG, "Failed to read services");
		}
	}

	private void goConnection(String stAddress )
	{
		try {
			if (mBluetoothDevice != null){
				int connState = mBluetoothManager.getConnectionState(mBluetoothDevice, BluetoothGatt.GATT);

				switch (connState) {
				case BluetoothGatt.STATE_CONNECTED:	
					Log.i(TAG, "STATE_CONNECTED");

					mBluetoothLeService.disconnect(mBluetoothDevice.getAddress());
					break;
				case BluetoothGatt.STATE_DISCONNECTED:
					Log.i(TAG, "STATE_DISCONNECTED");

					boolean ok = mBluetoothLeService.connect(mBluetoothDevice.getAddress());
					if (!ok) {
						Log.i(TAG, "Connect failed");
					}
					break;
				default:
					Log.i(TAG,"Device busy (connecting/disconnecting)");
					break;
				}
			}else{
				if (stAddress == null)
					connectedDeviceAddress= mUserConfig.getConfString(UserConfig.KV_DEVICE_BINDING_ADDRESS, null);
				else
					connectedDeviceAddress = stAddress;
				scanLeDevice(true);
			}
		}catch (Exception ex){

		}


	}

	private void watchWriteAlert(int iType)
	{
		try {
			BluetoothGattService serv = mBtGatt.getService(Watch.UUID_ALE_SERV);
			BluetoothGattCharacteristic charac = serv.getCharacteristic(Watch.UUID_ALE_DATA);

			if (charac == null)
			{
				Log.i(TAG, "UUID_ALE_DATA error");
				return;

			}
			byte[] val = new byte[1];
			if (iType == 1)
			{
				val[0] = 0x01;
			}else if (iType == 2)
			{
				val[0]  = 0x02;
			}else
			{
				val[0] = 0x00;
			}


			boolean bres =	charac.setValue(val);

			BLEDataOut data = new BLEDataOut();
			data.serv = serv;
			data.charac = charac;
			watchAddSend(data);

			//  bres = mBtLeService.writeCharacteristic(charac);

			//mBtLeService.waitIdle(GATT_TIMEOUT);
		}catch (Exception ex)
		{
			Log.i(TAG, "watchWriteString() ->" + ex.getMessage());

		}
	}

	private void watchSetKeyNotify(boolean bEnable)
	{
		try {
			BluetoothGattService serv = mBtGatt.getService(Watch.UUID_IKEY_SERV);
			BluetoothGattCharacteristic charac = serv.getCharacteristic(Watch.UUID_IKEY_DATA);

			if (charac == null)
			{
				Log.i(TAG, "UUID_LCD_TXT error");
				return;

			}
			BLEDataOut data = new BLEDataOut();
			data.serv = serv;
			data.charac = charac;
			data.isNotify = true;
			data.setNotify = bEnable;
			watchAddSend(data);

		}catch (Exception ex)
		{
			Log.i(TAG, "watchSetLed() ->" + ex.getMessage());

		}
	}

	private void watchSetLed(byte[] bValues)
	{
		try {
			BluetoothGattService serv = mBtGatt.getService(Watch.UUID_LED_SERV);
			BluetoothGattCharacteristic charac = serv.getCharacteristic(Watch.UUID_LED_DATA);

			if (charac == null)
			{
				Log.i(TAG, "UUID_LCD_TXT error");
				return;

			}

			boolean bres =	charac.setValue(bValues);

			BLEDataOut data = new BLEDataOut();
			data.serv = serv;
			data.charac = charac;
			watchAddSend(data);

		}catch (Exception ex)
		{
			Log.i(TAG, "watchSetLed() ->" + ex.getMessage());

		}
	}

	private void watchWriteString(String msg, int nline)
	{
		try {
			BluetoothGattService serv = mBtGatt.getService(Watch.UUID_KEY_SERV);
			BluetoothGattCharacteristic charac = serv.getCharacteristic(Watch.UUID_LCD_TXT);

			if (charac == null)
			{
				Log.i(TAG, "UUID_LCD_TXT error");
				return;

			}
			String buf = String.format("%d%s\0", nline, msg);
			byte[] value = buf.getBytes();

			boolean bres =	charac.setValue(value);

			BLEDataOut data = new BLEDataOut();
			data.serv = serv;
			data.charac = charac;
			watchAddSend(data);

			//  bres = mBtLeService.writeCharacteristic(charac);

			//mBtLeService.waitIdle(GATT_TIMEOUT);
		}catch (Exception ex)
		{
			Log.i(TAG, "watchWriteString() ->" + ex.getMessage());

		}
	}
	private void showClock()
	{
		String dateFormat = "yyyy-MM-dd";
		String timeFormat = "HH:mm";
		SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
		SimpleDateFormat stf = new SimpleDateFormat(timeFormat);
		String strbuf = stf.format(new Date());
		Random ran = new Random();

		watchWriteAlert((ran.nextInt(1000) % 2));

		//watchWriteString(strbuf, 4);
		//Log.i(TAG,"showClock " + strbuf);

		//watchWriteString(sdf.format(new Date()), 3);


	}
	private void BLE_Connected_WatchDog()
	{
		myWatchdogTimer = new Timer();
		myWatchdogTimer.schedule(new TimerTask() {          
			@Override
			public void run() {
				BLE_Handler(WATCHDOG_EVENT, 0, null);
			}

		}, 3000, WATCHDOG_TIMEOUT);
	}


	private void watchInit() {
		bleDataout = new ArrayList<BLEDataOut>();
		//	BLE_Connected_WatchDog();
		initReceiver();
		if (myProjectCode == 1){
			watchSetLostline();
			watchSetKeyNotify(true);

			myWatch.setOpmode(Watch.OPMODE_STANDBY);
		}
	}

	private void watchSetLostline()
	{
		boolean bEnable = mUserConfig.getConfBoolean(UserConfig.KV_SETTING_OUTOFRANGE, false);
		this.watchSetLed(bEnable? Watch.TAKE_LOSTLINK_ON : Watch.TAKE_LOSTLINK_OFF);
	}

	private void watchSendout()
	{
		if (bleDataout.size() > 0)
		{
			BLEDataOut bout = bleDataout.get(0);
			mBtLeService.waitIdle(GATT_TIMEOUT * 10);

			if (bout.isNotify)
			{
				mBtLeService.setCharacteristicNotification(bout.charac, bout.setNotify);
				bleDataout.remove(0); // not feedback.. so, remove now.
			}else{

				mBtLeService.writeCharacteristic(bout.charac);
			}				
			mBtLeService.waitIdle(GATT_TIMEOUT);

		}
	}

	private void watchAddSend(BLEDataOut data)
	{
		bleDataout.add(data);
		if (bleDataout.size() == 1)
			BLE_Handler(SEND_NEXT_DATA, 0, null);

	}

	private void onCharacteristicWrite(String uuidStr, int status) {
		Log.i(TAG,"onCharacteristicWrite: " + uuidStr);
		if (bleDataout.size() > 0)
			bleDataout.remove(0);
		BLE_Handler(SEND_NEXT_DATA, 0, null);
	}
	private void onCharacteristicsRead(String uuidStr, byte [] value, int status) {
		Log.i(TAG, "onCharacteristicsRead: " + uuidStr);
		//			if (uuidStr.equals(SensorTag.UUID_BAR_CALI.toString())) {
		//				Log.i(TAG, "CALIBRATED");
		//				// Barometer calibration values are read.
		//				List<Integer> cal = new ArrayList<Integer>();
		//				for (int offset = 0; offset < 8; offset += 2) {
		//					Integer lowerByte = (int) value[offset] & 0xFF; 
		//					Integer upperByte = (int) value[offset +1] & 0xFF;
		//					cal.add((upperByte << 8) + lowerByte);
		//				}
		//
		//				for (int offset = 8; offset < 16; offset += 2) {
		//					Integer lowerByte = (int) value[offset] & 0xFF; 
		//					Integer upperByte = (int) value[offset +1]; 
		//					cal.add((upperByte << 8) + lowerByte);
		//				}
		//
		//				BarometerCalibrationCoefficients.INSTANCE.barometerCalibrationCoefficients = cal;
		//			}
	}
	private void onCharacteristicChanged(String uuidStr, byte[] value) {
		Log.i(TAG,"onCharacteristicChanged: " + uuidStr);

		if (uuidStr.equals(Watch.UUID_IKEY_DATA.toString()))
		{
			BLE_Handler(ONKEYEVENT,(int) value[0], null);
		}
	}



	private void onKeyEvent(byte keycode) {
		// TODO Auto-generated method stub
		Log.d(TAG, String.format("onKeyEvent %d , %s", (int)keycode, myWatch.toString()));

		switch (myWatch.getOpmode())
		{
		case Watch.OPMODE_STANDBY:
			if (keycode == Watch.KEYEVENT_MOVE_LR || keycode == Watch.KEYEVENT_MOVE_RL){
				myWatch.slideKeyCounter = 1;
				activeKeyTimer(true);
				myWatch.setOpmode(Watch.OPMODE_TOUCH);
				watchSetLed(Watch.TOUCH_GROUPS_LED[myWatch.slideKeyCounter]);
			}
			break;
		case Watch.OPMODE_TOUCH:
			if (keycode == Watch.KEYEVENT_MOVE_LR || keycode == Watch.KEYEVENT_MOVE_RL){
				myWatch.slideKeyCounter++;
				if (myWatch.slideKeyCounter > 4)
					myWatch.slideKeyCounter = 1;

				watchSetLed(Watch.TOUCH_GROUPS_LED[myWatch.slideKeyCounter]);
				activeKeyTimer(true);
			}else if (keycode == Watch.KEYEVENT_PHOLD_L || keycode == Watch.KEYEVENT_PHOLD_R)
			{
				activeKeyTimer(false);
				launchApp(myWatch.slideKeyCounter);
				myWatch.setOpmode(Watch.OPMODE_STANDBY);

				watchSetLed(Watch.TAKE_OFF_LED);
			}
			break;
		case Watch.OPMODE_ALERT_CALLER:
			if (keycode == Watch.KEYEVENT_MOVE_LR || keycode == Watch.KEYEVENT_MOVE_RL)
			{
				Log.e(TAG,"OPMODE_ALERT_CALLER " + keycode);
				if (mUserConfig.getConfBoolean(UserConfig.KV_SETTING_REJECTCALLREPLY, false)){
					//TODO also check the recored file avaiable 
					//TODO :reject call
					//myWatch.setOpmode(Watch.OPMODE_REJECT_CALL);

				}else
				{
					//myWatch.setOpmode(Watch.OPMODE_STANDBY);

				}
				rejectCall(); 
				//watchSetLed(Watch.TAKE_OFF_LED);
			}
			break;
		case Watch.OPMODE_ALERT_APP:
		case Watch.OPMODE_REJECT_CALL:
			break;

		default:
			break;
		}


	}

	private void launchApp(int appNumber)
	{
		// launchApp
		String appName = mUserConfig.getConfString(UserConfig.KV_TOUCH0_TYPE.replace("{0}", String.valueOf(appNumber)), null);
		if (appName == null)
			return;

		if (UserConfig.ACTION_NAME_TAKE_PHOTO.equals(appName))
		{
			takePhoto();
		}else if (UserConfig.ACTION_NAME_NEXT_SONG.equals(appName))
		{
			mediaControl(MEDIACONTROL_KEY_NEXT);
		}else if (UserConfig.ACTION_NAME_PLAY_MUSIC.equals(appName))
		{
			mediaControl(MEDIACONTROL_KEY_PLAY);
		}else if (UserConfig.ACTION_NAME_PREV_SONG.equals(appName))
		{
			mediaControl(MEDIACONTROL_KEY_PREV);
		}else if (UserConfig.ACTION_NAME_GOOGLE_NEXT_SONG.equals(appName))
		{
			googlePlayControl(MEDIACONTROL_KEY_NEXT);
		}else if (UserConfig.ACTION_NAME_GOOGLE_PLAY_MUSIC.equals(appName))
		{
			googlePlayControl(MEDIACONTROL_KEY_PLAY);
		}else if (UserConfig.ACTION_NAME_GOOGLE_PREV_SONG.equals(appName))
		{
			googlePlayControl(MEDIACONTROL_KEY_PREV);
		}else if (UserConfig.ACTION_NAME_SPOTIFY_NEXT_SONG.equals(appName))
		{
			spotifyPlayControl(MEDIACONTROL_KEY_NEXT);
		}else if (UserConfig.ACTION_NAME_SPOTIFY_PLAY_MUSIC.equals(appName))
		{
			spotifyPlayControl(MEDIACONTROL_KEY_PLAY);
		}else if (UserConfig.ACTION_NAME_SPOTIFY_PREV_SONG.equals(appName))
		{
			spotifyPlayControl(MEDIACONTROL_KEY_PREV);
		}else if (UserConfig.ACTION_NAME_SEND_SMS.equals(appName))
		{
			String phoneName = mUserConfig.getConfString(UserConfig.KV_TOUCH0_SMS_NAME.replace("{0}", String.valueOf(appNumber)), null);
			String msg = mUserConfig.getConfString(UserConfig.KV_TOUCH0_SMS_MSG.replace("{0}", String.valueOf(appNumber)), null);
			String phonenum = com.playingsoft.android.tools.PhoneUtil.getContactNumberByName(MainService.this, phoneName);
			if (phonenum != null && msg!=null)
			{
				sendSMS(phonenum, msg);
			}else
			{
				Log.e(TAG, "No data to send SMS");
			}
		}

	}

	public final long KEYTIMEOUT = 5000;
	private void activeKeyTimer(boolean bActive)
	{
		myWatch.ClearKeyTimer();
		if (bActive){
			myWatch.keyTimer = new Timer();
			myWatch.keyTimer.schedule(new TimerTask(){

				@Override
				public void run() {
					if (myWatch.getOpmode() == Watch.OPMODE_TOUCH){
						myWatch.setOpmode(Watch.OPMODE_STANDBY);
						myWatch.slideKeyCounter =0;
						myWatch.ClearKeyTimer();
						watchSetLed(Watch.TOUCH_GROUPS_LED[myWatch.slideKeyCounter]);
					}
				}}, KEYTIMEOUT);
		}
	}

	public void sendSMS(String phoneNum, String textMsg)
	{
		try {
			SmsManager smsManager = SmsManager.getDefault();
			smsManager.sendTextMessage(phoneNum, null, textMsg, null, null);
		}catch (Exception ex)
		{
			ex.printStackTrace();
		}
	}


	public static final int MEDIA_TYPE_IMAGE = 1;
	public static final int MEDIA_TYPE_VIDEO = 2;

	private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
	private Uri fileUri;


	public void takePhoto() {
		Intent intent = new Intent(this, PhotoActivity.class);
		intent.putExtra("CAPTURENOW", true);
		intent.addCategory( Intent.CATEGORY_LAUNCHER );
		intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
		startActivity( intent );

		//Toast.makeText(getBaseContext(),"onVideoConfClick", Toast.LENGTH_SHORT).show();
		//Intent intent = new Intent( Intent.ACTION_MAIN );

		//		ComponentName cn = new ComponentName( "com.franklin.travelorganizer", "com.franklin.travelorganizer.CameraActivity" );
		//	intent.setComponent( cn );
		// intent.putExtra( "selectView", "GALLERY_VIEW" );


		// create Intent to take a picture and return control to the calling application
		//Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

		//	    Intent intent = new Intent("com.franklin.com.franklin.travelorganizer");
		//	    
		//	    
		//
		//	    fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image
		//	    intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
		//	    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
		//				| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
		//	    // start the image capture Intent
		//	    startActivity(intent);
	}

	private void answerPhoneHeadsethook(Context context) {
		// Simulate a press of the headset button to pick up the call
		Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON);		
		buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
		context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED");

		// froyo and beyond trigger on buttonUp instead of buttonDown
		Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);		
		buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
		context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
	}
	private ITelephony telephonyService;
	private void rejectCall()
	{
		TelephonyManager telephony = (TelephonyManager) 
				getSystemService(Context.TELEPHONY_SERVICE);  
		try {
			Class c = Class.forName(telephony.getClass().getName());
			Method m = c.getDeclaredMethod("getITelephony");
			m.setAccessible(true);
			telephonyService = (ITelephony) m.invoke(telephony);
			telephonyService.endCall();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}



	/** Create a file Uri for saving an image or video */
	private static Uri getOutputMediaFileUri(int type){
		return Uri.fromFile(getOutputMediaFile(type));
	}

	/** Create a File for saving an image or video */
	private static File getOutputMediaFile(int type){
		// To be safe, you should check that the SDCard is mounted
		// using Environment.getExternalStorageState() before doing this.

		File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
				Environment.DIRECTORY_PICTURES), "MyCameraApp");
		// This location works best if you want the created images to be shared
		// between applications and persist after your app has been uninstalled.

		// Create the storage directory if it does not exist
		if (! mediaStorageDir.exists()){
			if (! mediaStorageDir.mkdirs()){
				Log.d("MyCameraApp", "failed to create directory");
				return null;
			}
		}

		// Create a media file name
		String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
		File mediaFile;
		if (type == MEDIA_TYPE_IMAGE){
			mediaFile = new File(mediaStorageDir.getPath() + File.separator +
					"IMG_"+ timeStamp + ".jpg");
		} else if(type == MEDIA_TYPE_VIDEO) {
			mediaFile = new File(mediaStorageDir.getPath() + File.separator +
					"VID_"+ timeStamp + ".mp4");
		} else {
			return null;
		}

		return mediaFile;
	}







	private BroadcastReceiver appReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			final String action = intent.getAction();
			if (MainService.ACTION_APP_UPDATECONFIG.equals(action))
			{
				Log.d(TAG, action);
			}else if (MainService.ACTION_APP_BLE_SCANNING.equals(action))
			{
				boolean bres = (intent.getExtras().getBoolean(MainService.EXTRA_ENABLE , false));
				mScanOnly = bres;
				Log.d(TAG, action + " " + bres);

				scanLeDevice(bres);
			}else if (MainService.ACTION_APP_GO_CONNECTION.equals(action))
			{
				//mScanOnly = false;
				String addr = intent.getExtras().getString(MainService.EXTRA_DEVICE_ADDRESS, null);
				goConnection(addr);
			}
			else if (MainService.ACTION_UPDATE_LOST_LINK.equals(action))
			{
				watchSetLostline();
			}
		}
	};

	// ////////////////////////////////////////////////////////////////////////////////////////////////
	//
	// Broadcasted actions from Bluetooth adapter and BluetoothLeService
	//
	private BroadcastReceiver bleReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			final String action = intent.getAction();
			int status = intent.getIntExtra(BluetoothLeService.EXTRA_STATUS, BluetoothGatt.GATT_SUCCESS);

			if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
				// Bluetooth adapter state change
				switch (mBtAdapter.getState()) {
				case BluetoothAdapter.STATE_ON:
					Log.i(TAG, "bleReceiver - >STATE_ON");
					//	mConnIndex = NO_DEVICE;
					startBluetoothLeService();
					break;
				case BluetoothAdapter.STATE_OFF:
					Log.i(TAG, "bleReceiver - >STATE_OFF");
					stopBluetoothLeService();
					//Toast.makeText(context, R.string.app_closing, Toast.LENGTH_LONG).show();
					//	finish();
					break;
				default:
					Log.w(TAG, "Action STATE CHANGED not processed ");
					break;
				}

				//updateGuiState();
			} else if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
				// GATT connect
				mIsGattConnected = true;
				//android.os.Debug.waitForDebugger();
				mBtLeService = BluetoothLeService.getInstance();
				mBtGatt = BluetoothLeService.getBtGatt();
				mBluetoothDevice = BluetoothLeService.getBtGatt().getDevice();
				if (status == BluetoothGatt.GATT_SUCCESS) {
					Log.i(TAG, "ACTION_GATT_CONNECTED - >GATT_SUCCESS");
					//	discoverServices();
					BLE_Handler(BLE_JUST_CONNECTED, 0, null);

					//	setBusy(false);
					//	startDeviceActivity();
				} else{
					Log.i(TAG, "ACTION_GATT_CONNECTED - >GATT_FAILURE");

					//	setError("Connect failed. Status: " + status);
				}
			} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
				// GATT disconnect
				mIsGattConnected = false;
				//stopDeviceActivity();
				if (status == BluetoothGatt.GATT_SUCCESS) {
					Log.i(TAG, "ACTION_GATT_DISCONNECTED - >GATT_SUCCESS");
					onDisconnected() ;
					Intent bcDis = new Intent();
					bcDis.setAction(MainService.ACTION_DISCONNECTED_DEVICE);
					sendBroadcast(bcDis);	
					//setBusy(false);
					//mScanView.setStatus(mBluetoothDevice.getName() + " disconnected", STATUS_DURATION);
				} else {
					//setError("Disconnect failed. Status: " + status);  				
				}
				//mConnIndex = NO_DEVICE;
				//mBluetoothLeService.close();
			} else 	if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
				Log.i(TAG, "@_@ ACTION_GATT_SERVICES_DISCOVERED ");

				if (status == BluetoothGatt.GATT_SUCCESS) {
					BLE_Handler(BLE_AFTER_DISCOVERED, 0, null);

				} else {
					Toast.makeText(getApplication(), "Service discovery failed", Toast.LENGTH_LONG).show();
					return;
				}
			} else if (BluetoothLeService.ACTION_DATA_NOTIFY.equals(action)) {
				// Notification
				Log.i(TAG, "@_@ ACTION_DATA_NOTIFY ");

				byte  [] value = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA);
				String uuidStr = intent.getStringExtra(BluetoothLeService.EXTRA_UUID);
				onCharacteristicChanged(uuidStr, value);
			} else if (BluetoothLeService.ACTION_DATA_WRITE.equals(action)) {
				// Data written

				String uuidStr = intent.getStringExtra(BluetoothLeService.EXTRA_UUID);
				onCharacteristicWrite(uuidStr,status);
			} else if (BluetoothLeService.ACTION_DATA_READ.equals(action)) {
				// Data read
				Log.i(TAG, "@_@ ACTION_DATA_READ ");

				String uuidStr = intent.getStringExtra(BluetoothLeService.EXTRA_UUID);
				byte  [] value = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA);
				onCharacteristicsRead(uuidStr,value,status);
			}else {
				Log.w(TAG,"Unknown action: " + action);
			}

		}
	};

	// Code to manage Service life cycle.
	private final ServiceConnection mServiceConnection = new ServiceConnection() {

		public void onServiceConnected(ComponentName componentName, IBinder service) {
			mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
			if (!mBluetoothLeService.initialize()) {
				Log.d(TAG, "Unable to initialize BluetoothLeService");
				//	finish();
				return;
			}
			mBtGatt = BluetoothLeService.getBtGatt();

			//	android.os.Debug.waitForDebugger();

			final int n = mBluetoothLeService.numConnectedDevices();
			Log.d(TAG, "onServiceConnected -> numConnectedDevices " + n);
			//goConnection();
			/*
			if (n > 0) {
				runOnUiThread(new Runnable() {
					public void run() {
						mThis.setError("Multiple connections!");
					}
				});
			} else {
				startScan();
				Log.i(TAG, "BluetoothLeService connected");
			}*/
		}

		public void onServiceDisconnected(ComponentName componentName) {
			mBluetoothLeService = null;
			Log.i(TAG, "BluetoothLeService disconnected");
		}
	};


	private void mediaControl(int iControl)
	{

		Log.e(TAG, "mediaControl " + iControl);

		long eventtime = SystemClock.uptimeMillis(); 

		Intent downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 
		KeyEvent downEvent = new KeyEvent(eventtime, eventtime, 
				KeyEvent.ACTION_DOWN, iControl, 0); 
		downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent); 
		sendOrderedBroadcast(downIntent, null); 

		Intent upIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 
		KeyEvent upEvent = new KeyEvent(eventtime, eventtime, 
				KeyEvent.ACTION_UP, iControl, 0); 
		upIntent.putExtra(Intent.EXTRA_KEY_EVENT, upEvent); 
		sendOrderedBroadcast(upIntent, null); 


	}

	private void googlePlayControl(int iControl)
	{
		Intent i = new Intent();

		switch (iControl)
		{
		case MEDIACONTROL_KEY_PREV:
			i.setAction("com.android.music.musicservicecommand");
			i.putExtra("command", "previous");
			sendOrderedBroadcast(i, null);
			break;
		case MEDIACONTROL_KEY_NEXT:
			i.setAction("com.android.music.musicservicecommand");
			i.putExtra("command", "next");
			this.sendOrderedBroadcast(i, null);
			break;
		case MEDIACONTROL_KEY_PLAY:
			i.setAction("com.android.music.musicservicecommand.togglepause");
			this.sendOrderedBroadcast(i, null);
			break;
		}


	}

	// https://www.trustlook.com/report/1C8641E6BEDD11E06A65B56C59CE17ED/com.spotify.mobile.android.ui/#receivers
	// https://github.com/MohammadAG/Xposed-SView-MusicControls/blob/master/src/com/mohammadag/sviewpowerampmetadata/SViewPowerampMetadata.java
	private void spotifyPlayControl(int iControl)
	{
		Intent i = new Intent();
Log.i("Spotify", "action = " + iControl);
		switch (iControl)
		{
		case MEDIACONTROL_KEY_PREV:
			i.setAction("com.spotify.mobile.android.ui.widget.PREVIOUS");
			sendOrderedBroadcast(i, null);
			break;
		case MEDIACONTROL_KEY_NEXT:
			i.setAction("com.spotify.mobile.android.ui.widget.NEXT");
			sendOrderedBroadcast(i, null);
			break;
		case MEDIACONTROL_KEY_PLAY:
			i.setAction("com.spotify.mobile.android.ui.widget.PLAY");
			sendOrderedBroadcast(i, null);
			break;
		}


	}
















	// Device scan callback.
	// NB! Nexus 4 and Nexus 7 (2012) only provide one scan result per scan
	private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

		public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
			Log.d(TAG, "mLeScanCallback " + device.toString() + " " + mScanOnly );
			if (!mScanOnly)
			{
				BLE_Handler(BLE_SCAN_FOUND, rssi, device);
			}else {
				Intent intent = new Intent();
				intent.setAction(MainService.ACTION_SCANNED_DEVICE);
				intent.putExtra(MainService.EXTRA_DEVICE_NAME, device.getName());
				intent.putExtra(MainService.EXTRA_DEVICE_ADDRESS, device.getAddress());
				intent.putExtra(MainService.EXTRA_DEVICE_RSSI, rssi);
				sendBroadcast(intent);
			}
			/*
			runOnUiThread(new Runnable() {
				public void run() {
					// Filter devices
					if (checkDeviceFilter(device)) {
						if (!deviceInfoExists(device.getAddress())) {
							// New device
							BleDeviceInfo deviceInfo = createDeviceInfo(device, rssi);
							addDevice(deviceInfo);
						} else {
							// Already in list, update RSSI info
							BleDeviceInfo deviceInfo = findDeviceInfo(device);
							deviceInfo.updateRssi(rssi);
							mScanView.notifyDataSetChanged();
						}
					}
				}

			});

			 */
		}
	};
	public static final int BLE_SCAN_FOUND = 900;

	public static final int BLE_AFTER_DISCOVERED = 2000;
	public static final int BLE_JUST_CONNECTED = 2111;
	public static final int WATCHDOG_EVENT = 2234;
	public static final int SEND_NEXT_DATA = 1300;
	public static final int INCOMING_KEYEVENT = 1001;
	public static final int INCOMING_REMINDER_ALERT = 1111;
	public static final int INCOMING_EMAIL = 3333;
	public static final int INCOMING_SMS = 8888;
	public static final int INCOMING_CALL = 9000;
	public static final int INCOMING_USER_CMD = 9999;
	public static final int INCOMING_USERTIMER = 2222;
	public static final int INCOMING_CHECKER = 1234;
	public static final int ONKEYEVENT = 3332;





	private void BLE_Handler(int mType, int flag , Object obj)
	{
		myHandler.obtainMessage(mType, flag, 0, obj).sendToTarget();
	}


	/**
	 * Handler of incoming messages from clients.
	 */

	// myhandler
	class InternalHandler extends Handler {
		public InternalHandler(Looper looper) {
			super(looper);
		}
		@Override
		public void handleMessage(Message msg) {
			try{
				switch (msg.what) {
				case BLE_SCAN_FOUND:
					BluetoothDevice bleDevice = (BluetoothDevice) msg.obj;
					int rssiVal = msg.arg1;
					if (connectedDeviceAddress != null)
					{
						if (connectedDeviceAddress.equals(bleDevice.getAddress()))
						{
							scanLeDevice(false);
							final int n = mBluetoothLeService.numConnectedDevices();
							Log.d(TAG, "goConnection -> numConnectedDevices " + n);

							boolean bres = mBluetoothLeService.connect(connectedDeviceAddress);
							Log.i(TAG, "goConnection " + bres + " " + connectedDeviceAddress);

						}
					}

					break;
				case BLE_AFTER_DISCOVERED:
					Log.d(TAG, "@_@ BLE_AFTER_DISCOVERED");
					displayServices();

					break;
				case BLE_JUST_CONNECTED:
					Log.d(TAG, "@_@ BLE_JUST_CONNECTED");
					//displayServices();
					discoverServices();
					break;
				case WATCHDOG_EVENT:
					showClock();
					break;
				case SEND_NEXT_DATA:
					watchSendout();
					break;

				case INCOMING_CALL:
					int flag = msg.arg1;

					if (flag == CALL_RINGING)
					{
						String num =(String) msg.obj;
						incomingCallHandler(num);
					}
					else if (flag == CALL_ONHOOK)
					{
						onHookHandler(true);
					}else if (flag == CALL_OFFHOOK)
					{
						if (myProjectCode == 1){
							onHookHandler(false);

						}else{
							watchWriteString(getResources().getString(R.string.oled_in_call),2);
							if (myWatch.getOpmode() == Watch.OPMODE_REJECT_CALL)
							{
								AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

								if (am.getMode() == AudioManager.MODE_IN_CALL) {
									return;

									//									  AudioTrack track = new AudioTrack(AudioManager.STREAM_VOICE_CALL, mySampleRate, 
									//									            AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
									//									            myBuffersize, AudioTrack.MODE_STREAM);




								}

								// play record file to 
							}
						}
					}
					break;
				case INCOMING_SMS:
					appHandler("SMS");
					break;
				case INCOMING_EMAIL:
					appHandler("Mail");
					break;
				case INCOMING_REMINDER_ALERT:
					appHandler("Calendar");

					break;
				case ONKEYEVENT:
					onKeyEvent((byte) msg.arg1);
					break;
				default:
					super.handleMessage(msg);
					break;
				}
			}catch (Exception ex)
			{
				Log.e(TAG, "handler error (" +  msg.what + ") " + ex.getMessage());
			}
		}

		private void appHandler(String appName) {
			Log.e(TAG, "appHandler " + appName);

			try {
				for (int i = 1; i < 5 ;i++)
				{
					String sKey1 = UserConfig.KV_APP0_NOTIFY.replace("{0}", String.valueOf(i));
					String sKey2 = UserConfig.KV_APP0_NAME.replace("{0}", String.valueOf(i));
					if (mUserConfig.getConfBoolean(sKey1, false))
					{
						String sValue = mUserConfig.getConfString(sKey2, null);
						if (sValue!=null && sValue.equals(appName))
						{
							watchSetLed(Watch.APP_GROUPS_LED[i]);
							return ;
						}
					}
				}
			}catch (Exception ex)
			{
				ex.printStackTrace();
			}

		}
		private void onHookHandler(boolean bEnable)
		{
			// end of call
			if (myProjectCode == 1){
				if (inCallTimer != null){
					inCallTimer.cancel();
					inCallTimer = null;
				}
				watchSetLed(Watch.TAKE_OFF_LED);
				if (bEnable){
					showNotification("End call");
					myWatch.setOpmode(Watch.OPMODE_STANDBY);
				}
				else{
					showNotification("in call");
					myWatch.setOpmode(Watch.OPMODE_ALERT_CALLER);
				}


			}else
			{
				watchWriteString(getResources().getString(R.string.oled_end_call),2);
			}
			return ;

		}

		private void incomingCallHandler(String phoneNum)
		{
			String cname = null;
			boolean needRepeatTimer = true;
			

			cname = com.playingsoft.android.tools.PhoneUtil.getContactDisplayNameByNumber(MainService.this, phoneNum);

			final int grpnum = findCallerinGroup(cname);

			if (myProjectCode == 1){
				Log.e(TAG, "in-call3 " + grpnum);
				switch (grpnum)
				{
				case 0:
					watchSetLed(Watch.CALLER_VIP_LED);
					showNotification("VIP is calling");
					break;
				case 1:
					watchSetLed(Watch.CALLER_GROUP1_LED);
					showNotification("Group 1 is calling");
					break;
				case 2:
					watchSetLed(Watch.CALLER_GROUP2_LED);
					showNotification("Group 2 is calling");

					break;
				case 3:
					watchSetLed(Watch.CALLER_GROUP3_LED);
					showNotification("Group 3 is calling");

					break;
				case 4:
					watchSetLed(Watch.CALLER_GROUP4_LED);
					showNotification("Group 4 is calling");

					break;

				default:
					// for ungroup
					if (mUserConfig.getConfBoolean(UserConfig.KV_UNGROUP_NOTIFY, true))
					{
						watchSetLed(Watch.CALLER_UNGROUP_LED);
					}else
					{
						needRepeatTimer = false;
					}
					showNotification("Ungroup is calling");

					break;

				}
				if (inCallTimer != null)
					inCallTimer.cancel();
				if (needRepeatTimer){
					inCallTimer = new Timer();
					inCallTimer.schedule(new TimerTask() {
						@Override
						public void run() {
							{
								switch (grpnum)
								{
								case 0:
									watchSetLed(Watch.CALLER_VIP_LED);
									break;
								case 1:
									watchSetLed(Watch.CALLER_GROUP1_LED);
									break;
								case 2:
									watchSetLed(Watch.CALLER_GROUP2_LED);
									break;
								case 3:
									watchSetLed(Watch.CALLER_GROUP3_LED);
									break;
								case 4:
									watchSetLed(Watch.CALLER_GROUP4_LED);
									break;

								default:
									// for ungroup
									watchSetLed(Watch.CALLER_UNGROUP_LED);
									break;

								}

							}
						}

					},100, RESEND_LED_TIME);
				}
				myWatch.setOpmode(Watch.OPMODE_ALERT_CALLER);

			}else{

				watchWriteString(phoneNum,2);
				if (cname == null || !com.playingsoft.android.tools.PhoneUtil.isAscii(cname)){
					cname = "UNKNOWN";
				}
				final String disName = cname;
				new CountDownTimer(2000, 1000) {
					public void onTick(long millisUntilFinished) {
					}

					public void onFinish() {
						watchWriteString(disName, 1);

						//						if (grpnum >= 0){
						//							watchWriteString(String.format("Group %d",	grpnum) ,3);
						//						}else
						//						{
						//							watchWriteString(String.format("Not grp") ,3);
						//						}

					}
				}.start();
			}

			myWatch.setOpmode(Watch.OPMODE_ALERT_CALLER);



		}

		private int findCallerinGroup(String callerName)
		{

			if (callerName == null)
				return -1;
			String cname;


			if (mUserConfig.getConfBoolean(UserConfig.KV_VIP_NOTIFY, false))
			{
				cname = mUserConfig.getConfString(UserConfig.KV_VIP_NAME, null);
				if (cname != null)
				{
					if (cname.equals(callerName))
					{
						// found in VIP, return group 0 == VIP
						return 0;
					}
				}
			}

			for (int i = 1 ; i <= 4; i++)
			{		
				String grpnum = String.valueOf(i);

				String grpname =UserConfig.KV_CALL0_NOTIFY.replace("{0}", grpnum);

				if (mUserConfig.getConfBoolean(grpname, false))
				{
					String key2name = UserConfig.KV_CALL0_TOTAL_NAME.replace("{0}", grpnum);
					int numOfName = mUserConfig.getConfInt( key2name, 0);
					Log.d(TAG, grpname + " " + key2name + " " +numOfName);

					if (numOfName > 0)
					{
						for (int j = 0 ; j < numOfName ; j++)
						{
							String nameIndx = String.valueOf(j);
							String key3name = UserConfig.KV_CALL0_NAMES1.replace("{0}", grpnum).replace("{1}", nameIndx);
							cname = mUserConfig.getConfString(key3name, null);
							if (cname == null)
								continue;
							Log.d(TAG,  key3name + " " +cname);

							if (cname.equals(callerName))
							{
								// found in Group, return group number (i)
								return i;
							}						
						}

					}
				}
			}

			return -1; // not found in vip and group#, return -1;
		}

	}


	private void IncomingHandler(int mType, int flag , Object obj)
	{
		try {
			myHandler.obtainMessage(mType, flag, 0, obj).sendToTarget();
		}catch (Exception ex)
		{
			ex.printStackTrace();
		}
	}

	public final BroadcastReceiver mSMS = new BroadcastReceiver() {
		private Date checkedTime = new Date(0);

		@Override
		public void onReceive(Context context, Intent intent) {
			final Bundle bundle = intent.getExtras();
			try {
				Date onChangedDate = new Date();
				Log.e(TAG, "@_@ SmsReceiver.onReceiver" );

				if (bundle != null) {
					if ((onChangedDate.getTime() - checkedTime.getTime() > 2000) ){
						checkedTime = onChangedDate;
						IncomingHandler(INCOMING_SMS, 0, (Object[]) bundle.get("pdus"));
					}
				} // bundle is null

			} catch (Exception e) {
				Log.e("SmsReceiver", "Exception smsReceiver" +e);
			}
		}

	};

	public final BroadcastReceiver mCaller = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
			String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
			if (TelephonyManager.EXTRA_STATE_RINGING.equals(state))
			{
				Log.d(TAG, "Its Ringing");
				IncomingHandler(INCOMING_CALL, CALL_RINGING, number);
			}else if (TelephonyManager.EXTRA_STATE_IDLE.equals(state))
			{
				IncomingHandler(INCOMING_CALL, CALL_ONHOOK, null);
				Log.d(TAG, "Its Idle");

			}else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state))
			{
				IncomingHandler(INCOMING_CALL, CALL_OFFHOOK, null);
				Log.d(TAG, "Its OffHook");
			}
		}
	};

	class BLEDataOut 
	{
		public BluetoothGattService serv ;
		public BluetoothGattCharacteristic charac;
		public boolean isNotify = false;
		public boolean setNotify = false;


		public BluetoothGattService getServ() {
			return serv;
		}
		public void setServ(BluetoothGattService serv) {
			this.serv = serv;
		}
		public BluetoothGattCharacteristic getCharac() {
			return charac;
		}
		public void setCharac(BluetoothGattCharacteristic charac) {
			this.charac = charac;
		}

	}

	class KeyState
	{
		public byte key;
		public Date inTime;


	}


	private NotificationManager mNM;

	@SuppressWarnings("deprecation")
	private void showNotification(String text) {
		// Set the icon, scrolling text and timestamp
		Notification notification = new Notification(R.drawable.ic_launcher, text,
				System.currentTimeMillis());

		// The PendingIntent to launch our activity if the user selects this notification
		PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
				new Intent(this, com.franklin.icharm.MainActivity.class), 0);

		// Set the info for the views that show in the notification panel.
		notification.setLatestEventInfo(this, getText(R.string.app_name),
				text, contentIntent);

		// We show this for as long as our service is processing a command.
		notification.flags |= Notification.FLAG_ONGOING_EVENT;

		// Send the notification.
		// We use a string id because it is a unique number.  We use it later to cancel.
		mNM.notify(R.string.app_name, notification);
	}

	private void hideNotification() {
		mNM.cancel(R.string.app_name);
	}

	class ReminderContentObserver extends ContentObserver{
		private Date checkedTime = new Date(0);

		public ReminderContentObserver(Handler handler) {
			super(handler);
		}

		@Override
		public void onChange(boolean selfChange, Uri uri) {
			Date onChangedDate = new Date();
			if ((onChangedDate.getTime() - checkedTime.getTime() > 2000) ){
				checkedTime = onChangedDate;
				reloadReminders();
			}
		}

	}
	private ArrayList<scReminder> mReminders;
	private void reloadReminders()
	{
		if (emailAcc==null || emailAcc.length() == 0)
			return ;
		try {
			mReminders= scReminder.getReminders(this, emailAcc);
			setRemindersTimer();
		}catch (Exception ex)
		{
			Log.d(TAG, "reloadReminders " + ex.getLocalizedMessage());
		}
	}

	private Timer reminderTimer = null ;

	private void setRemindersTimer()
	{
		if (reminderTimer != null)
		{
			reminderTimer.cancel();
			reminderTimer = null;
		}
		if (mReminders.size() > 0)
		{ // only set the early one reminder
			reminderTimer = new Timer();
			reminderTimer.schedule(new TimerTask(){

				@Override
				public void run() {
					IncomingHandler(INCOMING_REMINDER_ALERT, 0, mReminders.get(0) );
				}

			} , mReminders.get(0).getReminderTime());
		}
	}


	class EmailContentObserver extends ContentObserver { 

		private Date checkedTime = new Date(0);
		public EmailContentObserver(Handler handler) { 
			super(handler); 
		}

		/* (non-Javadoc)
		 * @see android.database.ContentObserver#onChange(boolean, android.net.Uri)
		 */
		@Override
		public void onChange(boolean selfChange, Uri uri) {
			//super.onChange(selfChange, uri);
			Date onChangedDate = new Date();

			if (uri != null)
			{
				try {
					int unread = sClockEmail.getUnReadCount(uri, MainService.this);
					if (unread > 0)
					{
						Log.d(TAG, "@_@ INCOMING_EMAIL " ); 
						if ((onChangedDate.getTime() - checkedTime.getTime() > 2000) ){
							checkedTime = onChangedDate;
							IncomingHandler(INCOMING_EMAIL,  0 , uri);
						}

					}
				}catch (Exception ex)
				{
					ex.printStackTrace();
				}
			}

		}
	}

	public static boolean isConnected()
	{
		if (mBluetoothDevice != null)
			return mIsGattConnected;
		return false;
	} // isConnected

}
